﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Reflection;
using System.Threading.Tasks;
using Yuchen.NPA.Core;

namespace Yuchen.NPA.EntityFrameworkCore
{
    public class CommandExecutor<TEntity, TDbContext> : ICommandExecutor<TEntity>
    where TDbContext : DbContext
    where TEntity : class
    {
        private DbContext dbContext;
        public CommandExecutor(TDbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        public async Task<object> ExecuteAsync(MethodInfo methodInfo, params object[] args)
        {
            var dbSet = dbContext.Set<TEntity>();
            object result = null;
            switch (methodInfo.Name)
            {
                case "FindAll":
                    result = await dbSet.ToListAsync();
                    break;
                case "FindOne":
                    result = await dbSet.FindAsync(args);
                    break;
                case "Add":
                    result = (await dbSet.AddAsync((TEntity)args[0])).Entity;
                    break;
                case "Save":
                    await dbContext.SaveChangesAsync();
                    break;
                case "AddSave":
                    result = (await dbContext.AddAsync((TEntity)args[0])).Entity;
                    await dbContext.SaveChangesAsync();
                    break;
                case "Delete":
                    await DeleteAsync(dbSet, args, methodInfo.GetParameters());
                    break;
                case "DeleteSave":
                    await DeleteAsync(dbSet, args, methodInfo.GetParameters());
                    await dbContext.SaveChangesAsync();
                    break;
                case "Count":
                    result = await dbSet.LongCountAsync();
                    break;
                case "Exists":
                    result = (await dbSet.FindAsync(args[0])) != null;
                    break;
                default:
                    break;
            }

            return result;
        }

        private async Task DeleteAsync(DbSet<TEntity> dbSet, object[] args, ParameterInfo[] parameters)
        {
            if (parameters.Length <= 0)
            {
                throw new ArgumentOutOfRangeException("Delete 方法有且只能有一个参数");
            }
            var arg = args[0];

            var parameter = parameters[0];
            if (parameter.ParameterType == typeof(TEntity))
            {
                dbSet.Attach((TEntity)arg);
                dbSet.Remove((TEntity)arg);
            }
            else if (parameter.ParameterType == EntityMetaData.EntityKeys[typeof(TEntity)])
            {
                var result = await dbSet.FindAsync(arg);
                dbSet.Remove((TEntity)result);
            }
        }
    }
}
